package com.friend.service.user.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.common.core.constants.CacheConstants;
import com.common.core.constants.Constants;
import com.common.core.constants.HttpConstants;
import com.common.core.domain.LoginUser;
import com.common.core.domain.R;
import com.common.core.domain.vo.LoginUserVO;
import com.common.core.enums.ResultCode;
import com.common.core.enums.UserIdentify;
import com.common.core.enums.UserStatus;
import com.common.core.utils.ThreadLocalUtil;
import com.common.message.service.MailService;
import com.common.redis.service.RedisService;
import com.common.security.exception.ServiceException;
import com.common.security.service.TokenService;
import com.friend.domain.user.User;
import com.friend.domain.user.dto.UserDTO;
import com.friend.domain.user.dto.UserUpdateDTO;
import com.friend.domain.user.vo.UserVO;
import com.friend.manager.UserCacheManager;
import com.friend.manager.QuestionCacheManager;
import com.friend.mapper.user.UserMapper;
import com.friend.service.user.IUserService;
import com.friend.service.question.impl.QuestionServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private MailService mailService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserCacheManager userCacheManager;
    
    @Autowired
    private QuestionCacheManager questionCacheManager;
    
    @Autowired
    private QuestionServiceImpl questionService;
    
    @Value("${email.code-expiration}")
    private Long emailCodeExpiration;

    @Value("${email.send-limit}")
    private Integer sendLimit;

    @Value("${email.is-send}")
    private boolean isSend;

    @Value("${jwt.secret}")
    private String secret;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        if (!checkEmail(userDTO.getMail())) {
            throw new ServiceException(ResultCode.FAILED_USER_MAIL);
        }
        
        String mailCodeKey = getMailCodeKey(userDTO.getMail());
        Long expire = redisService.getExpire(mailCodeKey, TimeUnit.SECONDS);
        if (expire != null && (emailCodeExpiration * 60 - expire) < 60) {
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        
        // 每天邮箱验证码获取次数限制
        String codeTimeKey = getCodeTimeKey(userDTO.getMail()); // 改为邮箱
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
        
        // 调用邮箱服务发送验证码
        if (isSend) {
            boolean sendEmailCode = mailService.sendMail(userDTO.getMail());
            if (!sendEmailCode) {
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }
        
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(String email, String code) {
        checkCode(email, code);
        // 改为根据邮箱查询用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getEmail, email));
        if (user == null) {
            // 新用户注册逻辑
            user = new User();
            user.setEmail(email);
            user.setStatus(UserStatus.Normal.getValue());
            user.setCreateBy(Constants.SYSTEM_USER_ID);
            userMapper.insert(user);
        }
        
        // 用户登录时自动刷新ES索引和Redis缓存
        try {
            questionService.refreshQuestion();
            questionCacheManager.refreshCache();
        } catch (Exception e) {
            log.error("用户登录时刷新ES和Redis失败: email={}", email, e);
            // 不抛出异常，避免影响登录流程
        }
        
        return tokenService.createToken(user.getUserId(), secret, UserIdentify.ORDINARY.getValue(), 
                user.getNickName(), user.getHeadImage());
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.failed();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        // 返回头像获取接口 URL
        String headImage = loginUser.getHeadImage();
        if (StrUtil.isNotBlank(headImage)) {
            // 如果数据库中有头像，返回头像获取接口 URL（包含用户ID）
            Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
            // 通过网关访问，网关白名单需要在 Nacos 中配置
            // 添加时间戳参数避免浏览器缓存
            loginUserVO.setHeadImage("/dev-api/image/avatar/" + userId + "?t=" + System.currentTimeMillis());
        } else {
            // 如果没有头像，返回 null
            loginUserVO.setHeadImage(null);
        }
        return R.success(loginUserVO);
    }

    @Override
    public UserVO detail() {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        // 返回头像获取接口 URL
        String headImage = userVO.getHeadImage();
        if (StrUtil.isNotBlank(headImage)) {
            // 如果数据库中有头像，返回头像获取接口 URL（包含用户ID）
            // 通过网关访问，网关白名单需要在 Nacos 中配置
            // 添加时间戳参数避免浏览器缓存
            userVO.setHeadImage("/dev-api/image/avatar/" + userId + "?t=" + System.currentTimeMillis());
        } else {
            // 如果没有头像，返回 null
            userVO.setHeadImage(null);
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        // 头像字段由独立的 /user/head-image/update 接口处理，此处不处理
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    private void checkCode(String email, String code) {
        String emailCodeKey = getMailCodeKey(email);
        String cacheCode = redisService.getCacheObject(emailCodeKey, String.class);
        if (StrUtil.isEmpty(cacheCode)) {
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if (!cacheCode.equals(code)) {
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        redisService.deleteObject(emailCodeKey);
    }

    public static boolean checkEmail(String email) {
        // 邮箱的正则表达式
        Pattern regex = Pattern.compile("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$");
        Matcher m = regex.matcher(email);
        return m.matches();
    }


    private String getMailCodeKey(String email) {
        return CacheConstants.EMAIL_CODE_KEY + email;
    }

    private String getCodeTimeKey(String email) {
        return CacheConstants.CODE_TIME_KEY + email;
    }
}